﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace ECommon
{
    /// <summary>
    /// md5工具
    /// </summary>
    public static partial class Md5Tool
    {
        /// <summary>
        /// 计算大文件的32位小写md5值
        /// </summary>
        /// <param name="fileFullPath">大文件绝对路径</param>
        /// <returns>32位小写md5值</returns>
        /// <exception cref="ArgumentException">变量错误异常</exception>
        public static string GetMd5ByBigFile(string fileFullPath)
        {
            fileFullPath = PathTool.FormatFilePath(fileFullPath);
            if (!File.Exists(fileFullPath))
            {
                throw new FileNotFoundException(string.Format("文件不存在，路径：{0}", fileFullPath));
            }
            //缓冲大小
            int bufferSize = 2 * 1024 * 1024;
            byte[] buffer = new byte[bufferSize];
            using (Stream inputStream = File.Open(fileFullPath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                using (HashAlgorithm hashAlgorithm = SHA1.Create())
                {
                    //循环读写文件
                    byte[] output = new byte[bufferSize];
                    int readLength = inputStream.Read(buffer, 0, bufferSize);
                    while (readLength > 0)
                    {
                        //开始计算
                        hashAlgorithm.TransformBlock(buffer, 0, readLength, output, 0);
                        readLength = inputStream.Read(buffer, 0, bufferSize);
                    }
                    //完成计算
                    hashAlgorithm.TransformFinalBlock(buffer, 0, 0);
                    if (hashAlgorithm.Hash == null)
                    {
                        throw new ArgumentException("文件散列变量数组出错");
                    }
                    return BitConverter.ToString(hashAlgorithm.Hash).Replace("-", "");
                }
            }
        }

        /// <summary>
        /// 计算小文件的32位小写md5值
        /// </summary>
        /// <param name="fileFullPath">小文件绝对路径</param>
        /// <returns>32位小写md5值</returns>
        /// <exception cref="ArgumentNullException">参数为空异常</exception>
        /// <exception cref="Exception">文件过大异常</exception>
        public static string GetMd5BySmallFile(string fileFullPath)
        {
            fileFullPath = PathTool.FormatFilePath(fileFullPath);
            if (!File.Exists(fileFullPath))
            {
                throw new FileNotFoundException(string.Format("文件不存在，路径：{0}", fileFullPath));
            }
            using (FileStream fs = File.Open(fileFullPath, FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                if (fs.Length > 1024 * 1024 * 10)
                {
                    throw new Exception("文件过大，请使用API：“GetMd5ByBigFile”。");
                }
                using (MD5 md5 = MD5.Create())
                {
                    return StringTool.GetStringFromByteArray(md5.ComputeHash(fs));
                }
            }
        }

        /// <summary>
        /// 计算字符串的md5值
        /// </summary>
        /// <param name="utf8String">utf-8编码的字符串</param>
        /// <returns>32位小写md5值</returns>
        public static string GetMd5ByString(string utf8String)
        {
            if (string.IsNullOrEmpty(utf8String))
            {
                return utf8String;
            }
            using (MD5 md5 = MD5.Create())
            {
                return StringTool.GetStringFromByteArray(md5.ComputeHash(Encoding.UTF8.GetBytes(utf8String)));
            }
        }

        /// <summary>
        /// 根据当前时间获取md5
        /// </summary>
        /// <returns>32位小写md5值</returns>
        public static string GetMd5ByTimeTick()
        {
            return GetMd5ByString(DateTime.Now.Ticks.ToString());
        }


        /// <summary>
        /// 获取一个新的32位GUID小写字符串
        /// </summary>
        /// <returns>32位GUID小写字符串</returns>
        public static string GetGuid()
        {
            // 生成一个新的GUID
            Guid guid = Guid.NewGuid();
            // 将GUID转换为32位小写字符串（不带连字符）
            string guidLowercase = guid.ToString("N");
            return guidLowercase;
        }

        /// <summary>
        /// 获取Md5哈希后的盐值密码
        /// </summary>
        /// <param name="sourcePwd">初始密码，原始明文密码</param>
        /// <param name="saltVal">盐值</param>
        /// <returns>Md5哈希后的盐值密码</returns>
        /// <exception cref="ArgumentNullException">输入的变量出现异常</exception>
        /// <exception cref="ArgumentException">变量异常</exception>
        public static string GetSaltPwd(string sourcePwd, string saltVal)
        {
            if (string.IsNullOrWhiteSpace(sourcePwd))
            {
                throw new ArgumentNullException("密码不可为空");
            }
            if (string.IsNullOrWhiteSpace(saltVal))
            {
                throw new ArgumentNullException("盐值不可为空");
            }
            int sourcePwdLength = sourcePwd.Length;
            if (sourcePwdLength < 8 || sourcePwdLength > 32)
            {
                throw new ArgumentException("密码长度不符合要求，应该不短于8且不长于32");
            }
            int saltValLength = saltVal.Length;
            if (saltValLength != 32)
            {
                throw new ArgumentException("盐值长度不符合要求，应该为32");
            }
            string tmpPwd = sourcePwd.PadRight(32, '0');
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < 32; i++)
            {
                sb.Append(tmpPwd[i]);
                sb.Append(saltVal[i]);
            }
            return GetMd5ByString(sb.ToString());
        }
        /// <summary>
        /// 对密码进行加密
        /// </summary>
        /// <param name="sourcePwd">密码</param>
        /// <returns>加密后的密码</returns>
        /// <exception cref="ArgumentNullException">密码为空异常</exception>
        public static string PwdEncrypt(string sourcePwd)
        {
            if (string.IsNullOrWhiteSpace(sourcePwd))
            {
                throw new ArgumentNullException("密文不可为空");
            }
            byte[] b1 = Encoding.UTF8.GetBytes(sourcePwd);
            List<byte> bList = b1.ToList();
            List<byte> bRes = new List<byte>();
            bRes.AddRange(bList.Skip(1));
            bRes.Add(bList[0]);
            List<byte> b2 = bRes.Select(b => (byte)~b).ToList();
            return Convert.ToBase64String(b2.ToArray());
        }

        /// <summary>
        /// 对密码进行解密
        /// </summary>
        /// <param name="encryptPwd">密文</param>
        /// <returns>解密后的密码</returns>
        /// <exception cref="ArgumentNullException">密文为空异常</exception>
        public static string PwdDecrypt(string encryptPwd)
        {
            if (string.IsNullOrWhiteSpace(encryptPwd))
            {
                throw new ArgumentNullException("密码不可为空");
            }
            byte[] list = Convert.FromBase64String(encryptPwd);
            List<byte> listB2 = list.Select(b => (byte)~b).ToList();
            List<byte> ListbRes = new List<byte>();
            ListbRes.Add(listB2.Last());
            ListbRes.AddRange(listB2.Take(listB2.Count - 1));
            return Encoding.UTF8.GetString(ListbRes.ToArray());

        }
    }
}